package eu.trojanbug.spring.security.gae;

import java.io.IOException;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.Authentication;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.logout.LogoutHandler;
import org.springframework.security.util.UrlUtils;
import org.springframework.util.Assert;

import com.google.appengine.api.users.UserServiceFactory;

public class GoougleAccountsLogoutFilter extends SpringSecurityFilter implements InitializingBean {

    private String filterProcessesUrl = "/j_spring_security_logout";
    private List<LogoutHandler> handlers;    
    private String afterLogoutUrl="/";

    public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
            ServletException {

        if (requiresLogout(request, response)) {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();

            if (logger.isDebugEnabled()) {
                logger.debug("Logging out user '" + auth + "' and transferring to logout destination");
            }

            for (LogoutHandler handler : getHandlers()) {
                handler.logout(request, response, auth);
            }
            
            // redirect to logout page
            String googleAccountsLogoutURL = UserServiceFactory.getUserService().createLogoutURL(getAfterLogoutUrl());
            response.sendRedirect(googleAccountsLogoutURL);
            
            return;
        }

        chain.doFilter(request, response);
    }

    /**
     * Allow subclasses to modify when a logout should take place.
     *
     * @param request the request
     * @param response the response
     *
     * @return <code>true</code> if logout should occur, <code>false</code> otherwise
     */
    protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) {
        String uri = request.getRequestURI();
        int pathParamIndex = uri.indexOf(';');

        if (pathParamIndex > 0) {
            // strip everything from the first semi-colon
            uri = uri.substring(0, pathParamIndex);
        }

        int queryParamIndex = uri.indexOf('?');

        if (queryParamIndex > 0) {
            // strip everything from the first question mark
            uri = uri.substring(0, queryParamIndex);
        }

        if ("".equals(request.getContextPath())) {
            return uri.endsWith(filterProcessesUrl);
        }

        return uri.endsWith(request.getContextPath() + filterProcessesUrl);
    }

    public void setFilterProcessesUrl(String filterProcessesUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid value for" +
                " 'filterProcessesUrl'");
        this.filterProcessesUrl = filterProcessesUrl;
    }

    protected String getFilterProcessesUrl() {
        return filterProcessesUrl;
    }

    public int getOrder() {
        return FilterChainOrder.LOGOUT_FILTER;
    }

	@Override
	public void afterPropertiesSet() throws Exception {
        Assert.notEmpty(getHandlers(), "LogoutHandlers are required");
		
	}

	public void setHandlers(List<LogoutHandler> handlers) {
		this.handlers = handlers;
	}

	public List<LogoutHandler> getHandlers() {
		return handlers;
	}

	public void setAfterLogoutUrl(String afterLogoutUrl) {
		this.afterLogoutUrl = afterLogoutUrl;
	}

	public String getAfterLogoutUrl() {
		return afterLogoutUrl;
	}
}
